package com.agentfactory.teleoreactive.interpreter.mentalstate;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.agentfactory.logic.update.lang.IFormula;
import com.agentfactory.logic.update.lang.IMPLIES;
import com.agentfactory.logic.update.lang.Predicate;
import com.agentfactory.logic.update.reasoner.Bindings;
import com.agentfactory.logic.update.reasoner.ForwardChainer;
import com.agentfactory.logic.update.reasoner.IQueryEngine;
import com.agentfactory.logic.update.reasoner.IQueryable;
import com.agentfactory.logic.update.reasoner.QueryResult;
import com.agentfactory.logic.update.reasoner.ResolutionBasedQueryEngine;
import com.agentfactory.logic.update.util.DefaultLogicPresenter;
import com.agentfactory.teleoreactive.interpreter.Utilities;
enum BeliefType {percept, derived}
public class ModelTower implements IQueryable {
	
	class Belief{
		BeliefType TYPE;
		Predicate belief;
		Belief(BeliefType t, Predicate p){
			TYPE = t;
			belief = p;
		}
	}
	
	private List<Belief> percepts;
	private List<Belief> derivedBeliefs;
	private Map<String, List<Belief>> beliefMap;
	ForwardChainer perceptionT = new ForwardChainer();
	private List<Predicate> queryable;
	
	public ModelTower() {
		percepts = new LinkedList<Belief>();
		beliefMap = new HashMap<String, List<Belief>>();
		derivedBeliefs = new LinkedList<Belief>();
		queryable = new LinkedList<Predicate>();
		perceptionT.addSource(this);
	}

	public void addRule(IMPLIES rule) {
		Predicate res = (Predicate) rule.result();
		if (!queryable.contains(res))
			queryable.add(res);
		perceptionT.addRule(rule);
	}

	public void addBelief(Predicate belief) {
		List<Belief> list = beliefMap.get(belief.functor());
		Belief bel = new Belief(BeliefType.percept,belief);
		if (list == null) {
			list = new LinkedList<Belief>();
			list.add(bel);
			beliefMap.put(belief.functor(), list);
		} else {
			for (Belief b : list) {
				if (b.equals(bel)) {
					return;
				}
			}
			list.add(bel);
		}
		percepts.add(bel);
		updateBeliefs();
	}

	private void addDerivedBelief(Predicate belief) {
		List<Belief> list = beliefMap.get(belief.functor());
		Belief bel = new Belief(BeliefType.derived,belief);
		if (list == null) {
			list = new LinkedList<Belief>();
			list.add(bel);
			beliefMap.put(belief.functor(), list);
		} else {
			for (Belief b : list) {
				if (b.equals(bel)) {
					return;
				}
			}
			list.add(bel);
		}
		derivedBeliefs.add(bel);

	}

	public void updateBeliefs() {
		clearDerived();
		perceptionT.refresh();
		for (Predicate ruleResult : queryable) {
			System.out.println(Utilities.presenter.toString(ruleResult));
			QueryResult queryResult = perceptionT.query(ruleResult);
			if (queryResult.found) {
				for (Bindings binding : queryResult.bindings) {
					Predicate appliedRule = (Predicate) ruleResult.apply(binding);
					addDerivedBelief(appliedRule);
				}
			}
		}
		System.out.println(getBeliefs());

	}

	public void removeBelief(Predicate belief) {
		for(Belief b : percepts)
			if(b.belief.equals(belief)){
				percepts.remove(b);
				return;
			}
		for(Belief b : derivedBeliefs)
			if(b.belief.equals(belief)){
				percepts.remove(b);
				return;
			}
		List<Belief> list = beliefMap.get(belief.functor());
		for(Belief b: list){
			if(b.belief.equals(belief)){
				list.remove(b);
				return;
			}
		}
	}

	public boolean hasType(String type) {
		return true;
	}

	public List<IFormula> query(IFormula query) {
		List<IFormula> list = new LinkedList<IFormula>();
		if (Predicate.class.isInstance(query)) {
			Predicate predicate = (Predicate) query;
			List<Belief> bels1 = beliefMap.get(predicate.functor());
			List<Predicate> bels = new LinkedList<Predicate>();
			if (bels1 != null) {
				for(Belief b:bels1){
				bels.add(b.belief);
				}
				for (Predicate fos : bels) {
					if (fos.matches(predicate)) {
						list.add(fos);
					}
				}
			}
		}
		return list;
	}
	
	
	public void clearDerived(){
		derivedBeliefs.clear();
		for(List<Belief> lb: beliefMap.values()){
			List<Belief> clear = new LinkedList<Belief>();
			for(Belief b : lb){
				if(b.TYPE == BeliefType.derived){
					clear.add(b);
				}
			}
			lb.removeAll(clear);
		}
	}
	
	public void clearTower() {
		percepts.clear();
		beliefMap.clear();
		derivedBeliefs.clear();
	}

	public List<String> getBeliefs() {
		List<String> list = new LinkedList<String>();
/*		for(List<Predicate> lp: beliefMap.values()){
			for(Predicate p:lp)
				list.add(Utilities.presenter.toString(p));
		}*/
		for (Belief b : percepts)
			list.add(Utilities.presenter.toString(b.belief));
		for (Belief b :derivedBeliefs)
			list.add(Utilities.presenter.toString(b.belief));
		return list;
	}
}
